﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.LambdaExtensions;
using Peak.Core;
using Peak.Core.DataInterfaces;
using SharpArch.Data.NHibernate;
using System.Collections;

namespace Peak.Data
{
  public class StoryService : NHibernateRepositoryWithTypedId<Story, int>, IStoryRepository
  {
    public Page<Story> GetPage(StoryFilter filter)
    {
      ICriteria cFilter = Session.CreateCriteria(typeof (Story));

      if (filter.QuickFilter != null && filter.QuickFilter.Trim() != string.Empty)
      {
        string[] splited = filter.QuickFilter.Split(' ');

        Disjunction disjunction = Restrictions.Disjunction();
        Conjunction nameConjunction = Restrictions.Conjunction();
        foreach (string s in splited)
        {
          nameConjunction.Add(SqlExpression.InsensitiveLike<Story>(p => p.Name, s, MatchMode.Anywhere));
        }
        disjunction.Add(nameConjunction);

        Conjunction descConjunction = Restrictions.Conjunction();
        foreach (string s in splited)
        {
          descConjunction.Add(SqlExpression.InsensitiveLike<Story>(p => p.Description, s, MatchMode.Anywhere));
        }
        disjunction.Add(descConjunction);

        Conjunction catConjunction = Restrictions.Conjunction();
        foreach (string s in splited)
        {
          catConjunction.Add(SqlExpression.InsensitiveLike<Story>(p => p.Category, s, MatchMode.Anywhere));
        }
        disjunction.Add(catConjunction);

        Conjunction requesterConjunction = Restrictions.Conjunction();
        foreach (string s in splited)
        {
          requesterConjunction.Add(SqlExpression.InsensitiveLike<Story>(p => p.Requester, s, MatchMode.Anywhere));
        }
        disjunction.Add(requesterConjunction);

        Conjunction idConjunction = Restrictions.Conjunction();
        bool ok = false;
        foreach (string s in splited)
        {
          int id;
          if (int.TryParse(s, out id))
          {
            idConjunction.Add<Task>(f => f.Id == id);
            ok = true;
          }
        }
        if (ok)
        {
          disjunction.Add(idConjunction);
        }

        cFilter.Add(disjunction);
      }

      cFilter.Add<Story>(f => f.Project.Id == filter.ProjectId);

      if(filter.Status != 0)
      {
        Disjunction disjunction = Restrictions.Disjunction();
        if ((filter.Status & FilterStoryStatus.ToDo) == FilterStoryStatus.ToDo)
          disjunction.Add<Story>(f => f.Status == StoryStatus.ToDo);
        if ((filter.Status & FilterStoryStatus.Done) == FilterStoryStatus.Done)
          disjunction.Add<Story>(f => f.Status == StoryStatus.Done);
        if ((filter.Status & FilterStoryStatus.Verify) == FilterStoryStatus.Verify)
          disjunction.Add<Story>(f => f.Status == StoryStatus.Verify);
        if ((filter.Status & FilterStoryStatus.InProgress) == FilterStoryStatus.InProgress)
          disjunction.Add<Story>(f => f.Status == StoryStatus.InProgress);
        cFilter.Add(disjunction);        
      }

      if (filter.AssignedToSprint.HasValue)
      {
        if (filter.AssignedToSprint.Value)
        {
          cFilter.Add<Story>(f => f.CurrentSprintStory.Sprint == null);
        }
        else
        {
          cFilter.Add<Story>(f => f.CurrentSprintStory.Sprint != null);
        }
      }

      ICriteria cCount = cFilter.Clone() as ICriteria;
      cCount.SetProjection(Projections.RowCount());

      if (filter.PageSize.HasValue)
      {
        cFilter.SetFirstResult(filter.Page * filter.PageSize.Value);
        cFilter.SetMaxResults(filter.PageSize.Value);
      }

      Func<string,Order> order = Order.Asc; 
      if(filter.SortDirection == SortDirection.Desc)
        order = Order.Desc; 

      switch (filter.SortOrder) 
      {
        case StorySortOrder.Id:
          cFilter.AddOrder<Story>(f => f.Id, order);
          break;       
        case StorySortOrder.Name:
          cFilter.AddOrder<Story>(f => f.Name, order);
          break;
        case StorySortOrder.Sprint:
          Sprint sprint = null;
          cFilter.CreateAlias<Story>(f=>f.CurrentSprintStory.Sprint, ()=> sprint);
          cFilter.AddOrder(() => sprint.Name, order);
          break;        
        case StorySortOrder.Priority:
          cFilter.AddOrder<Story>(f => f.Priority, order).AddOrder<Story>(f => f.Name, order);
          break;
        case StorySortOrder.Status:
          cFilter.AddOrder<Story>(f => f.Status, order).AddOrder<Story>(f => f.Name, order);
          break;        
        case StorySortOrder.Category:
          cFilter.AddOrder<Story>(f => f.Category, order).AddOrder<Story>(f => f.Name, order);
          break;
        case StorySortOrder.StoryPoints:
          cFilter.AddOrder<Story>(f => f.StoryPoints, order).AddOrder<Story>(f => f.Name, order);
          break;
        default:
          throw new ArgumentOutOfRangeException();
      }

      IList list = cFilter.List();
      IList list2 = cCount.List();

      Page<Story> page = new Page<Story>();
      page.Results = list.OfType<Story>().ToArray();
      page.TotalCount = (int)(list2[0]);
      return page;      
    }    
  }
}